# Copyright (c) 2018 Presto Labs Pte. Ltd.
# Author: inkyu

import collections
import logging
import time
from tornado import gen

import jwt

import requests
from urllib.parse import urlencode
from coin.exchange.util.async_http_client import AsyncHttpClient
from coin.exchange.base.order_gateway import OrderSide, OrderType
from coin.exchange.upbit_v1.rate_limiter import UpbitRateLimiter


def _encode_payload(payload):
  return urlencode(payload)


def _order_side_to_str(order_side):
  if order_side == OrderSide.BUY:
    return 'bid'
  elif order_side == OrderSide.SELL:
    return 'ask'
  else:
    raise ValueError(order_side)


def _order_type_to_str(order_type):
  if order_type == OrderType.LIMIT:
    return 'limit'
  else:
    raise ValueError(order_type)


class UpbitPrivateClient(object):
  def __init__(self,
               config,
               logger=None,
               *,
               http_client=None,
               max_connections=4,
               order_timeout=60,
               rate_limiter=None):
    self._logger = logger or logging.getLogger(__name__)
    self._config = config
    self._session = requests.Session()
    self._http_client = http_client or AsyncHttpClient(max_connections=max_connections)
    self._order_timeout = order_timeout
    self.rate_limiter = rate_limiter or UpbitRateLimiter()

  def _generate_nonce(self):
    return str(int(round(time.time() * 1000000)))

  def get_auth(self, payload=None):
    payload_internal = collections.OrderedDict([
        ('access_key', self._config.access_key),
        ('nonce', self._generate_nonce()),
    ])
    if payload is not None:
      payload_internal['query'] = payload
    encoded = jwt.encode(payload_internal, self._config.secret_key, algorithm='HS256')
    return "Bearer %s" % (encoded.decode("utf-8"))

  @gen.coroutine
  def accounts(self):
    response = yield self._http_client.get('https://api.upbit.com/v1/accounts',
                                           headers={'Authorization': self.get_auth()})
    self.rate_limiter.update_default(response)
    return response

  @gen.coroutine
  def orders(self, *, market_id=None, state='wait', page=1, order_by='desc'):
    # TODO(inkyu): 'asc' for working order
    payload_dict = {
        'state': state,  # {wait, done, cancel}
        'page': page,  # 100 entries for a page?
        'order_by': order_by,  # {asc, desc}
    }
    if market_id is not None:
      payload_dict['market_id'] = market_id  # Native symbol
    payload = _encode_payload(payload_dict)
    response = yield self._http_client.get('https://api.upbit.com/v1/orders',
                                           params=payload,
                                           headers={'Authorization': self.get_auth(payload)})
    self.rate_limiter.update_default(response)
    return response

  @gen.coroutine
  def order(self, *, uuid):
    payload_dict = {
        'uuid': uuid,
    }
    payload = _encode_payload(payload_dict)
    response = yield self._http_client.get('https://api.upbit.com/v1/order',
                                           params=payload,
                                           headers={'Authorization': self.get_auth(payload)})
    self.rate_limiter.update_default(response)
    return response

  @gen.coroutine
  def submit_order(self,
                   *,
                   native_symbol,
                   order_side,
                   price_str,
                   qty_str,
                   order_type,
                   identifier=None):
    if identifier is not None:
      # Identifier must be unique so we cannot use order_id yet.
      raise NotImplementedError()
    payload = _encode_payload({
        'market': native_symbol,
        'side': _order_side_to_str(order_side),
        'volume': qty_str,
        'price': price_str,
        'ord_type': _order_type_to_str(order_type)
    })
    response = yield self._http_client.post('https://api.upbit.com/v1/orders',
                                            data=payload,
                                            headers={
                                                'Authorization': self.get_auth(payload),
                                                'Content-Type': 'application/x-www-form-urlencoded',
                                            })
    self.rate_limiter.update_order(response)
    return response

  @gen.coroutine
  def cancel_order(self, exchange_order_id):
    payload = _encode_payload({'uuid': exchange_order_id})
    response = yield self._http_client.delete('https://api.upbit.com/v1/order',
                                              params=payload,
                                              headers={'Authorization': self.get_auth(payload)})
    self.rate_limiter.update_default(response)
    return response
